﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DwarfParser
{
    class AstPrintVisitor : AstNodeVisitor
    {
        private string result = "";
        private int level = 0;

        public string PrintTree(AsTree tree)
        {
            level = 0;
            tree.GetRootNode().Accept(this);
            return result;
        }

        private void NextLine()
        {
            PushText("\n");
            for (int i = 0; i < level; ++i )
            {
                PushText(" ");
            }
        }

        private void PushText(string str)
        {
            result += str;
        }

        private void AddMargin()
        {
            level += 4;
        }

        private void SubMargin()
        {
            level -= 4;
        }

        public override bool Visit(AstMainClass node)
        {
            PushText("class ");
            node.ClassName.Accept(this);
            NextLine();
            PushText("{");
            AddMargin();
            NextLine();
            node.EntryFunction.Accept(this);
            SubMargin();
            NextLine();
            PushText("}");

            return false;
        }
        
        public override bool Visit(AstStatements node)
        {            
            PushText("{");
            AddMargin();
            NextLine();
            int count = node.Statements.Count;
            for (int i = 0; i < count; ++i )
            {
                node.Statements[i].Accept(this);
                if (i != count - 1)
                {
                    NextLine();
                }
            }
            SubMargin();
            NextLine();
            PushText("}");
            return false;
        }

        public override bool Visit(AstMethodDecl node)
        {            
            node.AccessModifier.Accept(this);
            node.StaticModifier.Accept(this);
            node.Type.Accept(this);
            node.FunctionName.Accept(this);
            node.ArgumentsList.Accept(this);
            NextLine();
            node.Statements.Accept(this);            
            return false;
        }

        public override bool Visit(AstFunctionArgumentsList node)
        {
            PushText("(");
            int count = node.Declarations.Count;
            for (var i = 0; i < count; ++i )
            {
                AstArgumentDecl decl = node.Declarations[i];
                decl.Accept(this);

                if (i < count - 1)
                {
                    PushText(", ");
                }
            }
            PushText(")");
            return false;
        }

        public override bool Visit(AstArgumentDecl node)
        {
            return true;
        }

        public override bool Visit(AstAccessModifier node)
        {
            if (node.Access == AstAccessModifier.AccessType.PUBLIC)
            {
                PushText("public ");
            }
            return false;
        }

        public override bool Visit(AstType node)
        {
            PushText(MyMaps.AstTypeToString(node.type) + " ");
            return false;
        }
        
        public override bool Visit(AstStaticModifier node)
        {
            PushText("static ");
            return false;
        }

        public override bool Visit(AstInteger node)
        {
            PushText(node.Value);
            return false;
        }

        public override bool Visit(AstId node)
        {
            PushText(node.Value);
            return false;
        }
        
        public override bool Visit(AstDotExpression node)
        {
            node.LeftOperand.Accept(this);
            PushText(".");
            node.ClassField.Accept(this);
            return false;
        }

        public override bool Visit(AstFunctionCall node)
        {
            node.Expr.Accept(this);
            PushText("(");
            node.ArgumentsList.Accept(this);
            PushText(");");            
            return false;
        }

        public override bool Visit(AstFunctionCallWithReturnVal node)
        {
            node.Expr.Accept(this);
            PushText("(");
            node.ArgumentsList.Accept(this);
            PushText(")");
            return false;
        }

        public override bool Visit(AstArgumentsList node)
        {
            int count = node.ExpressionList.Count;
            for (int i = 0; i < count; ++i)
            {                
                node.ExpressionList[i].Accept(this);

                if (i > 0 && i < count - 1)
                {
                    PushText(", ");
                }
            }

            return false;
        }

        public override bool Visit(AstWhileStatement node)
        {
            PushText("while (");
            node.Condition.Accept(this);
            PushText(")");
            node.Statements.Accept(this);
            return false;
        }

        public override bool Visit(AstArrayAccess node)
        {
            node.Id.Accept(this);
            PushText("[");
            node.Indexer.Accept(this);
            PushText("]");
            return false;
        }

        public override bool Visit(AstLogicalExpression node)
        {
            node.LeftOperand.Accept(this);
            PushText(" " + node.Operator.ToString() + " ");            
            node.RightOperand.Accept(this);
            return false;
        }

        public override bool Visit(AstAddExpression node)
        {
            node.LeftOperand.Accept(this);
            PushText(" + ");
            node.RightOperand.Accept(this);
            return false;
        }

        public override bool Visit(AstMinusExpression node)
        {
            node.LeftOperand.Accept(this);
            PushText(" - ");
            node.RightOperand.Accept(this);
            return false;
        }

        public override bool Visit(AstMultiplicationExpression node)
        {
            node.LeftOperand.Accept(this);
            PushText(" * ");
            node.RightOperand.Accept(this);
            return false;
        }

        public override bool Visit(AstDivisionExpression node)
        {
            node.LeftOperand.Accept(this);
            PushText(" / ");
            node.RightOperand.Accept(this);
            return false;
        }

        public override bool Visit(AstAssigmentExpression node)
        {
            node.LeftOperand.Accept(this);
            PushText(" = ");
            node.RightOperand.Accept(this);
            PushText(";");
            return false;
        }

        public override bool Visit(AstIfStatement node)
        {
            PushText("if (");
            node.Condition.Accept(this);
            PushText(")");
            NextLine();
            node.ThenStatements.Accept(this);
            if (node.ElseStatementsOpt != null)
            {
                NextLine();
                PushText("else");
                NextLine();
                node.ElseStatementsOpt.Accept(this);
            }

            return false;
        }
    }
}
